package com.duobaoyu.dby.supply.biz.service.order.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.duobaoyu.dby.channel.adapter.enums.ChannelCodeEnum;
import com.duobaoyu.dby.channel.adapter.enums.biz.order.OrderExceptionTypeEnum;
import com.duobaoyu.dby.common.mybatis.support.ParamConvert;
import com.duobaoyu.dby.common.mybatis.util.EnumUtils;
import com.duobaoyu.dby.supply.biz.client.boundary.order.bean.bo.OrderBO;
import com.duobaoyu.dby.supply.biz.client.boundary.order.bean.bo.SaveOrderRepairBO;
import com.duobaoyu.dby.supply.biz.converter.OrderConverter;
import com.duobaoyu.dby.supply.biz.enums.OrderRepairStatusEnum;
import com.duobaoyu.dby.supply.biz.exception.SupplyChainBizException;
import com.duobaoyu.dby.supply.biz.service.order.OrderRepairService;
import com.duobaoyu.dby.supply.biz.util.IdGeneratorUtil;
import com.duobaoyu.dby.supply.common.biz.bo.order.OrderRepairBO;
import com.duobaoyu.dby.supply.common.biz.bo.order.OrderRepairRecordBO;
import com.duobaoyu.dby.supply.common.biz.bo.order.UpdateOrderRepairBO;
import com.duobaoyu.dby.supply.common.biz.bo.order.UpdateOrderRepairRecordBO;
import com.duobaoyu.dby.supply.common.biz.dto.config.PreWarningConditionDTO;
import com.duobaoyu.dby.supply.common.biz.dto.order.OrderRepairPageDTO;
import com.duobaoyu.dby.supply.common.biz.vo.order.OrderRepairPageVO;
import com.duobaoyu.dby.supply.core.common.exception.CustomAssert;
import com.duobaoyu.dby.supply.db.entity.order.OrderRepair;
import com.duobaoyu.dby.supply.db.entity.order.OrderRepairRecord;
import com.duobaoyu.dby.supply.db.repository.order.OrderRepairRecordRepository;
import com.duobaoyu.dby.supply.db.repository.order.OrderRepairRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 补单
 *
 * @author chenms
 * @date 2023/10/17 1:53 PM
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderRepairServiceImpl implements OrderRepairService {

    private final OrderConverter orderConverter;
    private final OrderRepairRepository orderRepairRepository;
    private final OrderRepairRecordRepository orderRepairRecordRepository;

    @Override
    public boolean saveRepairOrderList(List<SaveOrderRepairBO> orderRepairs) {
        if (CollectionUtils.isEmpty(orderRepairs)) {
            return false;
        }
        List<OrderRepair> orderRepairList = orderConverter.toOrderRepairs(orderRepairs);
        return orderRepairRepository.saveBatch(orderRepairList);
    }

    @Override
    public SaveOrderRepairBO genSaveOrderRepair(OrderBO order, String errorCode, String errorMessage) {
        SaveOrderRepairBO saveOrderRepair = new SaveOrderRepairBO();
        saveOrderRepair.setChannelOrderAmount(order.getChannelOrderAmount());
        saveOrderRepair.setOrderSn(order.getOrderSn());
        saveOrderRepair.setSupplyTradeSn(order.getSupplyTradeSn());
        saveOrderRepair.setBizTradeSn(order.getBizTradeSn());
        saveOrderRepair.setShowServerName(order.getShowServerName());
        saveOrderRepair.setChannelCode(order.getChannelCode());
        saveOrderRepair.setRepairStatus(OrderRepairStatusEnum.WAIT_REPAIR.getValue());
        saveOrderRepair.setMchId(order.getMchId());


        OrderExceptionTypeEnum orderExceptionTypeEnum = OrderExceptionTypeEnum.convertFromGlobalErrorCode(errorCode);
        saveOrderRepair.setExceptionType(orderExceptionTypeEnum.getValue());
        saveOrderRepair.setFailDesc(errorMessage);
        return saveOrderRepair;
    }

    @Override
    public Page<OrderRepairPageVO> page(OrderRepairPageDTO orderRepairPage) {
        LambdaQueryWrapper<OrderRepair> queryWrapper = getOrderRepairLambdaQueryWrapper(orderRepairPage);
        Page<OrderRepair> page = orderRepairRepository.page(ParamConvert.getPage(orderRepairPage), queryWrapper);
        List<OrderRepairPageVO> orderRepairPageList = orderConverter.toOrderRepairPageList(page.getRecords());
        orderRepairPageList.forEach(item -> {
            item.setExceptionTypeStr(OrderExceptionTypeEnum.getDescByType(item.getExceptionType()));
            item.setRepairStatusStr(OrderRepairStatusEnum.getDescByStatus(item.getRepairStatus()));
        });
        return new Page<OrderRepairPageVO>(page.getCurrent(), page.getSize(), page.getTotal()).setRecords(orderRepairPageList);
    }


    @Override
    public long count(OrderRepairPageDTO orderRepairPage) {
        LambdaQueryWrapper<OrderRepair> queryWrapper = getOrderRepairLambdaQueryWrapper(orderRepairPage);
        return orderRepairRepository.count(queryWrapper);
    }

    @Override
    public List<OrderRepairBO> statusCheck(List<String> orderSnList, List<OrderRepairStatusEnum> expectStatusList) {
        List<OrderRepair> orderRepairList = orderRepairRepository.list(Wrappers.lambdaQuery(OrderRepair.class).in(OrderRepair::getOrderSn, orderSnList));
        if (CollectionUtils.isEmpty(orderRepairList)) {
            SupplyChainBizException.fail("异常订单不存在");
        }
        if (orderRepairList.size() != orderSnList.size()) {
            // 找出不存在的订单号
            List<String> orderSns = orderRepairList.stream().map(OrderRepair::getOrderSn).collect(Collectors.toList());
            Collection<String> subtract = CollectionUtils.subtract(new HashSet<>(orderSnList), orderSns);
            if (CollectionUtils.isNotEmpty(subtract)) {
                SupplyChainBizException.fail(String.format("部分异常订单不存在:%s", subtract));
            }
        }
        if (CollectionUtils.isNotEmpty(expectStatusList)) {
            List<String> notSupportOrderSnList = orderRepairList.stream().filter(item -> {
                OrderRepairStatusEnum orderRepairStatusEnum = EnumUtils.valueOf(OrderRepairStatusEnum.class, item.getRepairStatus());
                return !expectStatusList.contains(orderRepairStatusEnum);
            }).map(OrderRepair::getOrderSn).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(notSupportOrderSnList)) {
                SupplyChainBizException.fail(String.format("当前异常订单状态不支持该操作:%s", notSupportOrderSnList));
            }
        }
        return orderConverter.toOrderRepairList(orderRepairList);
    }

    @Override
    public OrderRepairRecordBO saveRepairRecord(OrderRepairBO orderRepair) {
        OrderRepairRecord orderRepairRecord = new OrderRepairRecord();
        orderRepairRecord.setOrderSn(orderRepair.getOrderSn());
        Integer repairingStatus = OrderRepairStatusEnum.REPAIRING.getValue();
        orderRepairRecord.setRepairStatus(repairingStatus);
        // 如果为预下单成功,重新确认是,补单订单号与原订单号一致
        String repairOrderSn;
        if (Optional.ofNullable(orderRepair).map(OrderRepairBO::getIsConfirmOrder).orElse(Boolean.FALSE)) {
            repairOrderSn = orderRepairRecord.getOrderSn();
        } else {
            repairOrderSn = IdGeneratorUtil.getOrderSn(ChannelCodeEnum.getChannelCodeEnumByCode(orderRepair.getChannelCode()).getCode(), orderRepair.getShowServerName());
        }
        orderRepairRecord.setRepairOrderSn(repairOrderSn);
        boolean save = orderRepairRecordRepository.save(orderRepairRecord);
        CustomAssert.isTrue(save, "保存补单记录失败");
        return orderConverter.toOrderRepairRecord(orderRepairRecord);
    }

    @Override
    public void updateOrderRepairStatusAndErrorInfo(UpdateOrderRepairBO updateOrderRepair) {
        List<String> orderSnList = updateOrderRepair.getOrderSnList();
        CustomAssert.notEmpty(orderSnList, "更新异常订单,订单号不能为空");
        Integer newStatus = updateOrderRepair.getNewStatus();
        // 是否为补单操作
        boolean isRepairing = EnumUtils.eq(OrderRepairStatusEnum.REPAIRING, newStatus);
        CustomAssert.notNull(isRepairing, String.format("未知的补单状态%s", newStatus));
        List<Integer> orderRepairStatusEnums = OrderRepairStatusEnum.getPreStatusList(newStatus);
        int effectRows = orderRepairRepository.updateByWrapper(Wrappers.lambdaUpdate(OrderRepair.class)
                .set(Objects.nonNull(newStatus), OrderRepair::getRepairStatus, newStatus)
                .set(StrUtil.isNotBlank(updateOrderRepair.getFailDesc()), OrderRepair::getFailDesc, updateOrderRepair.getFailDesc())
                .set(Objects.nonNull(updateOrderRepair.getExceptionType()), OrderRepair::getExceptionType, updateOrderRepair.getExceptionType())
                .set(isRepairing, OrderRepair::getLastRepairTime, LocalDateTime.now())
                .setSql(isRepairing, "`repair_times`=`repair_times`+1")
                .setSql("`version`=`version`+1")
                .in(OrderRepair::getOrderSn, orderSnList)
                .in(CollectionUtils.isNotEmpty(orderRepairStatusEnums), OrderRepair::getRepairStatus, orderRepairStatusEnums));
        if (effectRows != orderSnList.size()) {
            log.warn("更新异常单结果不正确,期望行数={},实际={},更新数据={}", orderSnList.size(), effectRows, JSON.toJSONString(updateOrderRepair));
            SupplyChainBizException.fail("更新异常单失败,数据发生变动,请重试!");
        }
    }

    @Override
    public void updateRepairRecordStatusAndErrorInfo(UpdateOrderRepairRecordBO repairRecord) {
        CustomAssert.hasLength(repairRecord.getOrderSn(), "订单号不能为空");
        List<Integer> orderRepairStatusEnums = OrderRepairStatusEnum.getPreStatusList(repairRecord.getNewStatus());
        LambdaUpdateWrapper<OrderRepairRecord> recordLambdaUpdateWrapper = Wrappers.lambdaUpdate(OrderRepairRecord.class)
                .set(Objects.nonNull(repairRecord.getNewStatus()), OrderRepairRecord::getRepairStatus, repairRecord.getNewStatus())
                .set(StrUtil.isNotBlank(repairRecord.getFailDesc()), OrderRepairRecord::getFailDesc, repairRecord.getFailDesc())
                .set(StrUtil.isNotBlank(repairRecord.getFailDesc()), OrderRepairRecord::getFailCode, repairRecord.getExceptionType())
                .set(StrUtil.isNotBlank(repairRecord.getChannelTradeOrderSn()), OrderRepairRecord::getChannelTradeOrderSn, repairRecord.getChannelTradeOrderSn())
                .setSql("`version`=`version`+1")
                .eq(StrUtil.isNotBlank(repairRecord.getOrderSn()), OrderRepairRecord::getOrderSn, repairRecord.getOrderSn())
                .in(CollectionUtils.isNotEmpty(orderRepairStatusEnums), OrderRepairRecord::getRepairStatus, orderRepairStatusEnums);
        // 根据订单号更新,只更新最后一条
        if (Objects.isNull(repairRecord.getId())) {
            recordLambdaUpdateWrapper.orderByDesc(OrderRepairRecord::getId).last("limit 1");
        }
        // 更新补单记录
        orderRepairRecordRepository.update(recordLambdaUpdateWrapper);

        // 更新补单数据
        orderRepairRepository.updateByWrapper(Wrappers.lambdaUpdate(OrderRepair.class)
                .set(Objects.nonNull(repairRecord.getNewStatus()), OrderRepair::getRepairStatus, repairRecord.getNewStatus())
                .set(OrderRepair::getFailDesc, repairRecord.getFailDesc())
                .set(OrderRepair::getExceptionType, repairRecord.getExceptionType())
                .setSql("`version`=`version`+1")
                .in(OrderRepair::getOrderSn, repairRecord.getOrderSn())
                .in(CollectionUtils.isNotEmpty(orderRepairStatusEnums), OrderRepair::getRepairStatus, orderRepairStatusEnums));
    }

    @Override
    public OrderRepairBO getByOrderSn(String orderSn, boolean returnRecordList) {
        OrderRepair orderRepair = orderRepairRepository.getOne(Wrappers.lambdaQuery(OrderRepair.class).eq(OrderRepair::getOrderSn, orderSn));
        OrderRepairBO orderRepairBo = orderConverter.toOrderRepair(orderRepair);
        if (Objects.nonNull(orderRepairBo) && returnRecordList) {
            List<OrderRepairRecord> orderRepairRecordList = orderRepairRecordRepository.list(Wrappers.lambdaQuery(OrderRepairRecord.class).eq(OrderRepairRecord::getOrderSn, orderSn));
            orderRepairBo.setOrderRepairRecords(orderConverter.toOrderRepairRecordList(orderRepairRecordList));
        }
        return orderRepairBo;
    }

    @Override
    public List<OrderRepairRecordBO> listOrderRepairRecord(List<String> orderSnList, List<Integer> statusList) {
        List<OrderRepairRecord> list = orderRepairRecordRepository.list(Wrappers.lambdaQuery(OrderRepairRecord.class).in(OrderRepairRecord::getOrderSn, orderSnList)
                .in(CollectionUtils.isNotEmpty(statusList), OrderRepairRecord::getRepairStatus, statusList));
        return orderConverter.toOrderRepairRecordList(list);
    }

    /**
     * 获取查询条件
     *
     * @param orderRepairPage
     * @return
     */
    private LambdaQueryWrapper<OrderRepair> getOrderRepairLambdaQueryWrapper(OrderRepairPageDTO orderRepairPage) {
        LambdaQueryWrapper<OrderRepair> queryWrapper = Wrappers.lambdaQuery(OrderRepair.class)
                .eq(Objects.nonNull(orderRepairPage.getRepairStatus()), OrderRepair::getRepairStatus, orderRepairPage.getRepairStatus())
                .and(StrUtil.isNotBlank(orderRepairPage.getOrderKeyword()),
                        (wrapper) -> wrapper.eq(OrderRepair::getOrderSn, orderRepairPage.getOrderKeyword())
                                .or().eq(OrderRepair::getSupplyTradeSn, orderRepairPage.getOrderKeyword())
                                .or().eq(OrderRepair::getBizTradeSn, orderRepairPage.getOrderKeyword()))
                .eq(StrUtil.isNotBlank(orderRepairPage.getChannelCode()), OrderRepair::getChannelCode, orderRepairPage.getChannelCode())
                .eq(StrUtil.isNotBlank(orderRepairPage.getMchId()), OrderRepair::getMchId, orderRepairPage.getMchId())
                .eq(Objects.nonNull(orderRepairPage.getOrderExceptionType()), OrderRepair::getExceptionType, orderRepairPage.getOrderExceptionType())
                .eq(StrUtil.isNotBlank(orderRepairPage.getServerName()), OrderRepair::getShowServerName, orderRepairPage.getServerName())
                .ge(StrUtil.isNotBlank(orderRepairPage.getStartTime()), OrderRepair::getCreateTime, String.format("%s 00:00:00", orderRepairPage.getStartTime()) )
                .le(StrUtil.isNotBlank(orderRepairPage.getEndTime()), OrderRepair::getCreateTime, String.format("%s 23:59:59", orderRepairPage.getEndTime()))
                .orderByDesc(OrderRepair::getId);
        PreWarningConditionDTO preWarningCondition = orderRepairPage.getPreWarningCondition();
        if (Objects.nonNull(preWarningCondition)) {
            queryWrapper.in(CollectionUtils.isNotEmpty(preWarningCondition.getStatus()), OrderRepair::getRepairStatus, preWarningCondition.getStatus());
        }
        return queryWrapper;
    }

}
